package top.fullj.eventbus;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.concurrent.Executor;

/**
 * @author bruce.wu
 * @since 2022/10/25 12:54
 */
class DelegateUiExecutor implements EventExecutor {

    static final DelegateUiExecutor INSTANCE = new DelegateUiExecutor();

    private static final String CLS_ANDROID_HANDLER = "android.os.Handler";
    private static final String CLS_ANDROID_LOOPER = "android.os.Looper";
    private static final String CLS_AWT_EVENT_QUEUE = "java.awt.EventQueue";

    private final Executor executor;

    private DelegateUiExecutor() {
        this.executor = newExecutor();
    }

    private Executor newExecutor() {
        if (isAndroid()) {
            return new AndroidExecutor();
        }
        if (isAwt()) {
            return new AwtExecutor();
        }
        return new BadExecutor();
    }

    @Override
    public String threadMode() {
        return ThreadMode.UI;
    }

    @Override
    public void execute(Runnable command) {
        executor.execute(command);
    }

    private boolean isAndroid() {
        try {
            Class.forName(CLS_ANDROID_HANDLER);
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }

    private boolean isAwt() {
        try {
            Class.forName(CLS_AWT_EVENT_QUEUE);
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }

    private static class BadExecutor implements Executor {
        @Override
        public void execute(Runnable command) {
            throw new IllegalStateException("Bad Executor");
        }
    }

    private static class AwtExecutor implements Executor {

        private Method invokeLaterMethod = null;

        AwtExecutor() {
            try {
                Class<?> clsEventQueue = Class.forName(CLS_AWT_EVENT_QUEUE);
                this.invokeLaterMethod = clsEventQueue.getMethod("invokeLater", Runnable.class);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void execute(Runnable command) {
            if (invokeLaterMethod == null) {
                throw new NullPointerException("invokeLaterMethod == null");
            }
            try {
                invokeLaterMethod.invoke(null, command);
            } catch (ReflectiveOperationException e) {
                throw new RuntimeException(e);
            }
        }

    }

    private static class AndroidExecutor implements Executor {

        private Object handler = null;
        private Method postMethod = null;

        AndroidExecutor() {
            try {
                Class<?> clsLooper = Class.forName(CLS_ANDROID_LOOPER);
                Method getMainLooper = clsLooper.getDeclaredMethod("getMainLooper");
                Object mainLooper = getMainLooper.invoke(null);

                Class<?> clsHandler = Class.forName(CLS_ANDROID_HANDLER);
                Constructor<?> handlerCtor = clsHandler.getConstructor(clsLooper);

                this.postMethod = clsHandler.getMethod("post", Runnable.class);
                this.handler = handlerCtor.newInstance(mainLooper);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void execute(Runnable command) {
            if (handler == null) {
                throw new NullPointerException("handler == null");
            }
            if (postMethod == null) {
                throw new NullPointerException("postMethod == null");
            }
            try {
                postMethod.invoke(handler, command);
            } catch (ReflectiveOperationException e) {
                throw new RuntimeException(e);
            }
        }

    }

}
